/********************************** (C) COPYRIGHT *******************************
 * File Name          : CH394Q_SPI.C
 * Author             : WCH
 * Version            : V1.0.1
 * Date               : 2024/12/09
 * Description        : Hardware abstraction layer for CH39Q SPI V1.0.0
 *******************************************************************************/
#include "ch394q_spi.h"
#include "xspi.h"
#include "xgpio.h"


extern XGpio gpio;
extern XSpi ch394q_spi;

#define CH349Q_CS   0X04               //M20

//cs low
void SPICH394QStart(void)
{
    uint32_t gpio_status = XGpio_DiscreteRead(&gpio, 1);
    XGpio_DiscreteWrite(&gpio, 1, gpio_status & (~CH349Q_CS));//CS LOW
}

//cs high
void SPIEndCH394Q(void)
{
    uint32_t gpio_status = XGpio_DiscreteRead(&gpio, 1);
    XGpio_DiscreteWrite(&gpio, 1, gpio_status | CH349Q_CS);//CS HIGH
}
// extern SPI_HandleTypeDef hspi1;
/*******************************************************************************
 * Function Name  : CH394Q_SPIPort_Init
 * Description    : SPI port initialization
 * Input          : None
 * Return         : None
 *******************************************************************************/
void CH394Q_SPIPort_Init(XSpi *SpiInstancePtr, uint16_t SpiDeviceId)
{
    int Status;
    u32 ControlReg;
    //	u32 Count;
    //	u8 Test;
    XSpi_Config *ConfigPtr; /* Pointer to Configuration data */

    // memset(Spi1_SendBuf, 0x00, 1024);
    // memset(Spi1_ReadBuf, 0x00, 1024);
    /*
     * Initialize the SPI driver so that it is  ready to use.
     */
    ConfigPtr = XSpi_LookupConfig(SpiDeviceId);
    if (ConfigPtr == NULL)
    {
        return XST_DEVICE_NOT_FOUND;
    }

    Status = XSpi_CfgInitialize(SpiInstancePtr, ConfigPtr,
                                ConfigPtr->BaseAddress);
    if (Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }

    /*
     * Perform a self-test to ensure that the hardware was built correctly.
     */
    Status = XSpi_SelfTest(SpiInstancePtr);
    if (Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }

    /*
     * Run loopback test only in case of standard SPI mode.
     */
    if (SpiInstancePtr->SpiMode != XSP_STANDARD_MODE)
    {
        return XST_SUCCESS;
    }

    /*
     * Set the Spi device as a master and in loopback mode.
     */
    /*
    Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION |
                    XSP_LOOPBACK_OPTION);
    */
    Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION | XSP_MANUAL_SSELECT_OPTION | XSP_CLK_ACTIVE_LOW_OPTION | XSP_CLK_PHASE_1_OPTION); //|XSP_MANUAL_SSELECT_OPTION
    if (Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }

    Status = XSpi_SetSlaveSelect(SpiInstancePtr, 0x01);
    if (Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }

    /*
     * Start the SPI driver so that the device is enabled.
     */
    XSpi_Start(SpiInstancePtr);

    /*
     * Disable Global interrupt to use polled mode operation
     */
    XSpi_IntrGlobalDisable(SpiInstancePtr);

    ControlReg = XSpi_GetControlReg(SpiInstancePtr);
    ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
    XSpi_SetControlReg(SpiInstancePtr, ControlReg);

    return XST_SUCCESS;
}

/*******************************************************************************
 * Function Name  : SPI1_WriteRead
 * Description    : SPI read and write one byte
 * Input          : uint8_t Data
 * Return         : None
 *******************************************************************************/
uint8_t SPI1_WriteRead(uint8_t data)
{
    // while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    // SPI_I2S_SendData(SPI1, data);
    // while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    // return SPI_I2S_ReceiveData(SPI1);
    uint8_t receivedData = 0;
    // HAL_StatusTypeDef status = HAL_ERROR;
    // status = HAL_SPI_TransmitReceive(&hspi1, &data, &receivedData, 1, 0xff);
    // if (status != HAL_OK)
    // {
    //     Error_Handler();
    // }
    // else
    // {
    // 	return receivedData;
    // }
    
}

/******************************************************************************
 * Function Name  : SPIWriteCH394QData
 * Description    : SPI write data
 * Input          : uint8_t mdata
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPIWriteCH394QData(uint8_t mdata)
{
    // SPI1_WriteRead(mdata);
    while ((XSpi_ReadReg(ch394q_spi.BaseAddr, XSP_SR_OFFSET) & XSP_SR_TX_EMPTY_MASK) == 0);//wait for transmit empty
    XSpi_WriteReg(ch394q_spi.BaseAddr, XSP_DTR_OFFSET, mdata);
    while ((XSpi_ReadReg(ch394q_spi.BaseAddr, XSP_SR_OFFSET) & XSP_SR_TX_EMPTY_MASK) == 0);//wait for transmit empty
}

/*******************************************************************************
 * Function Name  : SPIReadCH394QData
 * Description    : SPI read data
 * Input          : None
 * Output         : None
 * Return         : uint8_t i
 *******************************************************************************/
uint8_t SPIReadCH394QData(void)
{
    // uint8_t i;
    // i = SPI1_WriteRead(0x00);
    // return i;
    uint8_t receive_data;
    while ((XSpi_ReadReg(ch394q_spi.BaseAddr, XSP_SR_OFFSET) & XSP_SR_TX_EMPTY_MASK) == 0);//wait for transmit empty
    XSpi_WriteReg(ch394q_spi.BaseAddr, XSP_DTR_OFFSET, 0x0c);
    while ((XSpi_ReadReg(ch394q_spi.BaseAddr, XSP_SR_OFFSET) & XSP_SR_TX_EMPTY_MASK) == 0);//wait for transmit empty
    while ((XSpi_GetStatusReg(&ch394q_spi) & XSP_SR_RX_FULL_MASK) == 0);//wait for fifo is empty
    receive_data = (uint8_t)XSpi_ReadReg(ch394q_spi.BaseAddr, XSP_DRR_OFFSET);
    return receive_data;
}


/******************************************************************************
 * Function Name  : CH394Q_Write
 * Description    : Write a byte
 * Input          : addr: Write data address
                    data: Write data
 * Output         : None
 * Return         : None
 *******************************************************************************/
void CH394Q_Write(uint32_t addr, uint8_t data)
{
    SPICH394QStart();
    SPIWriteCH394QData((addr & 0x00ff0000) >> 16);
    SPIWriteCH394QData((addr & 0x0000ff00) >> 8);
    SPIWriteCH394QData((addr & 0x000000f8) + 4);
    SPIWriteCH394QData(data);
    SPIEndCH394Q();
}

/******************************************************************************
 * Function Name  : CH394Q_Read
 * Description    : Read a byte data
 * Input          : addr: Read data address
 * Output         : None
 * Return         : data
 *******************************************************************************/
uint8_t CH394Q_Read(uint32_t addr)
{
    uint8_t data = 0;
    SPICH394QStart();
    SPIWriteCH394QData((addr & 0x00ff0000) >> 16);
    SPIWriteCH394QData((addr & 0x0000ff00) >> 8);
    SPIWriteCH394QData((addr & 0x000000f8));
    data = SPIReadCH394QData();
    SPIEndCH394Q();
    return data;
}

/******************************************************************************
 * Function Name  : CH394Q_WriteBuf
 * Description    : Write len byte data
 * Input          : addr: Write data address
                    buf: Write data
                    len: Length
 * Output         : None
 * Return         : len
 *******************************************************************************/
uint16_t CH394Q_WriteBuf(uint32_t addr, uint8_t *buf, uint16_t len)
{
#if (SPI_MODE == 1)
    uint16_t i = 0;
    SPICH394QStart();
    SPIWriteCH394QData((addr & 0x00ff0000) >> 16);
    SPIWriteCH394QData((addr & 0x0000ff00) >> 8);
    SPIWriteCH394QData((addr & 0x000000f8) + 4);
    for (i = 0; i < len; i++)
    {
        SPIWriteCH394QData(buf[i]);
    }
    SPIEndCH394Q();
#elif (SPI_DMA_MODE == 1)
    SPICH394QStart();
    while ((SPI1->SR & SPI_I2S_FLAG_TXE) == 0);
    SPI1->DR = (addr & 0x00ff0000) >> 16;
    while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == 0);
    (void)(SPI1->DR);
    while ((SPI1->SR & SPI_I2S_FLAG_TXE) == 0);
    SPI1->DR = (addr & 0x0000ff00) >> 8;
    while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == 0);
    (void)(SPI1->DR);
    while ((SPI1->SR & SPI_I2S_FLAG_TXE) == 0);
    SPI1->DR = (addr & 0x000000f8) + 4;
    while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == 0);
    (void)(SPI1->DR);
    SPI1->CR2 |= SPI_I2S_DMAReq_Tx;
    DMA1_Channel3->CNDTR = len;
    DMA1_Channel3->CMAR = (uint32_t)buf;
    DMA1_Channel3->CCR |= DMA_CCR1_EN;
    while ((!DMA_GetFlagStatus(DMA1_FLAG_TC3)));
    while ((SPI1->SR) & SPI_I2S_FLAG_BSY);
    SPIEndCH394Q();
    DMA_ClearFlag(DMA1_FLAG_TC3);
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, DISABLE);
    DMA_Cmd(DMA1_Channel3, DISABLE);
    (void)SPI1->DR;
#endif
    return len;
}

/******************************************************************************
 * Function Name  : CH394Q_ReadBuf
 * Description    : Read len byte data
 * Input          : addr: Read data address
                    buf: Store read data
                    len: Length
 * Output         : None
 * Return         : len
 *******************************************************************************/
uint16_t CH394Q_ReadBuf(uint32_t addr, uint8_t *buf, uint16_t len)
{
#if (SPI_MODE == 1)
    uint16_t i = 0;
    SPICH394QStart();
    SPIWriteCH394QData((addr & 0x00ff0000) >> 16);
    SPIWriteCH394QData((addr & 0x0000ff00) >> 8);
    SPIWriteCH394QData((addr & 0x000000f8));
    for (i = 0; i < len; i++)
    {
        buf[i] = SPIReadCH394QData();
    }
    SPIEndCH394Q();
#elif (SPI_DMA_MODE == 1)
    SPICH394QStart();
    while ((SPI1->SR & SPI_I2S_FLAG_TXE) == 0);
    SPI1->DR = (addr & 0x00ff0000) >> 16;
    while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == 0);
    (void)(SPI1->DR);
    while ((SPI1->SR & SPI_I2S_FLAG_TXE) == 0);
    SPI1->DR = (addr & 0x0000ff00) >> 8;
    while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == 0);
    (void)(SPI1->DR);
    while ((SPI1->SR & SPI_I2S_FLAG_TXE) == 0);
    SPI1->DR = (addr & 0x000000f8);
    while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == 0);
    (void)(SPI1->DR);
    SPI1->CR2 |= SPI_I2S_DMAReq_Tx;
    SPI1->CR2 |= SPI_I2S_DMAReq_Rx;
    DMA1_Channel3->CNDTR = len;
    DMA1_Channel3->CMAR = (uint32_t)buf;
    DMA1_Channel2->CNDTR = len;
    DMA1_Channel2->CMAR = (uint32_t)buf;
    DMA1_Channel3->CCR |= DMA_CCR1_EN;
    DMA1_Channel2->CCR |= DMA_CCR1_EN;
    while ((!DMA_GetFlagStatus(DMA1_FLAG_TC2)) && (!DMA_GetFlagStatus(DMA1_FLAG_TC3)));
    while ((SPI1->SR) & SPI_I2S_FLAG_BSY);
    SPIEndCH394Q();
    DMA_ClearFlag(DMA1_FLAG_TC3);
    DMA_ClearFlag(DMA1_FLAG_TC2);
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, DISABLE);
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, DISABLE);
    DMA_Cmd(DMA1_Channel3, DISABLE);
    DMA_Cmd(DMA1_Channel2, DISABLE);
#endif
    return len;
}
